package main

import (
	"context"
	"grpc_etcd/proto"
	"fmt"
	"go.etcd.io/etcd/clientv3"
	"google.golang.org/grpc"
	"os"
	"time"
)
/**
grpc客户端
版本号变更法：etcd对于每一个key的put一次后起version会加1.我们使用此version模上(%)服务注册表数量，得到模值。得到的数有轮循的效果。

{
	"header": {
		"cluster_id": "14841639068965178418",
		"member_id": "10276657743932975437",
		"revision": "6466",
		"raft_term": "3"
	},
	"kvs": [
		{
			"key": "cnBjX3NlcnZlcl9sYl9rZXk=",
			"create_revision": "6325",
			"mod_revision": "6465",
			"version": "91",
			"value": "MQ=="
		}
	],
	"count": "1"
}
 */

const lbkey  = "rpc_server_lb_key" //使用此key来获取version变更次数

func main(){
	conf := &RpcClientConf{
		endpoints:[]string{"http://localhost:2379"},
		key:"student_server",
	}
	cli3,err := clientv3.New(clientv3.Config{Endpoints:conf.endpoints})
	if err!=nil{
		panic(err)
	}
	//ticker + for 循环实现多客户端并发请求rpc
	t := time.NewTicker( 2 * time.Second)
	for{
		rpcClient,err := NewRpcClient(conf, func(conn *grpc.ClientConn) {
			//创建远程链接客户端
			studentServerClient := proto.NewStudentServerClient(conn)
			student,err := studentServerClient.GetStudent(context.TODO(),&proto.ReqStudent{Id:3})
			if err!=nil{
				fmt.Println(err.Error())
				return
			}
			fmt.Println(student)
		})
		if err !=nil {
			panic(err)
		}
		rpcClient.Do(cli3)

		<-t.C
	}
}

type ExecuteRpcFunc func(conn *grpc.ClientConn)

type RpcClient struct{
	key string
	executerpcFunc ExecuteRpcFunc
}
type RpcClientConf struct{
	endpoints []string
	key string
}
func NewRpcClient(conf *RpcClientConf,fc ExecuteRpcFunc)(*RpcClient,error){
	return &RpcClient{
		executerpcFunc:fc,
		key:conf.key,
	},nil
}
func (rpcClient *RpcClient)Do(cli3 *clientv3.Client){
	//服务发现，获取etcd里的服务地址信息
	getResp,err := cli3.Get(context.TODO(),rpcClient.key,clientv3.WithPrefix())//通过前缀获取所有相匹配的服务
	if err!=nil{
		panic(err)
	}
	serverMap := map[int]string{}
	for i,kv := range getResp.Kvs {
		serverMap[i] = string(kv.Value)
	}
	if len(serverMap) >0{
		//lbkey 负载均衡key 存储入etcd
		cli3.Put(context.TODO(),lbkey,"1")
		gr,err := cli3.Get(context.TODO(),lbkey)
		//获取lbkey的更新次数
		version := 0
		for _, kv := range gr.Kvs {
			version = int(kv.Version)
		}
		/**
		取模方式获取
		 */
		index := version % len(serverMap) //[0,len(serverMap))

		//dial链接到随机选择的rpc服务
		ctx,_:= context.WithTimeout(context.TODO(),5 * time.Second)
		conn,err := grpc.DialContext(ctx,serverMap[index],grpc.WithInsecure())
		if err!=nil{
			//链接建立失败
			panic(err)
		}
		fmt.Println("请求rpc服务：",serverMap[index])
		//执行远程rpc方法
		rpcClient.executerpcFunc(conn)
	}else {
		fmt.Println("所有rpc服务已断开...")
		os.Exit(1)
	}
}

//默认rpc调用方式写法
func defversion1(){
	conn,err := grpc.Dial("127.0.0.1:8080",grpc.WithInsecure())
	if err!=nil{
		panic(err)
	}
	//创建远程链接客户端
	studentServerClient := proto.NewStudentServerClient(conn)
	student,err := studentServerClient.GetStudent(context.TODO(),&proto.ReqStudent{Id:3})
	if err!=nil{
		panic(err)
	}
	fmt.Println(student)
}